package pl.com.qapps.datastore.berkeley.impl;

import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import pl.com.qapps.datastore.BytesHelper;
import pl.com.qapps.datastore.berkeley.SinglePropertyKey;

import com.google.appengine.api.datastore.DataTypeTranslator;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
import com.google.storage.onestore.v3.OnestoreEntity.Property;
import com.google.storage.onestore.v3.OnestoreEntity.PropertyValue;
import com.sleepycat.bind.EntityBinding;
import com.sleepycat.bind.tuple.TupleOutput;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.SecondaryDatabase;
import com.sleepycat.je.SecondaryMultiKeyCreator;

public class SinglePropertyKeyCreator implements SecondaryMultiKeyCreator {

	private final EntityBinding<EntityProto> entityBinding;
	private final boolean reverseValue;

	public SinglePropertyKeyCreator(EntityBinding<EntityProto> entityBinding,
			boolean reverseValue) {
		this.entityBinding = entityBinding;
		this.reverseValue = reverseValue;
	}

	@Override
	public void createSecondaryKeys(SecondaryDatabase secondary,
			DatabaseEntry key, DatabaseEntry data, Set<DatabaseEntry> results) {

		EntityProto entity = entityBinding.entryToObject(key, data);
		for (Property property : entity.propertys()) {
			// since using proto, propertys() are indexed and rawPropertys() are
			// not
			// if (property.)
			// || property.getValue() == null)
			// continue;
			// index property
			byte[] value;
			try {
				// since using proto there are properties like Blob or String
				// value = PropertyIndexer.serialize(property.getValue());
				value = composePropertyKey(property);
				if (reverseValue && value != null)
					value = BytesHelper.reverseArray(value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
				throw new RuntimeException(
						"Failed create single indexes, serialization exception",
						e);
			}
			if (value != null)
				results.add(new SinglePropertyKey(entity.getKindUri(), property
						.getName(), value));

		}

		// for (Entry<String, Object> property :
		// entity.getProperties().entrySet()) {
		// if (entity.isUnindexedProperty(property.getKey())
		// || property.getValue() == null)
		// continue;
		// // index property
		// byte[] value;
		// try {
		// value = PropertyIndexer.serialize(property.getValue());
		// if (reverseValue)
		// value = BytesHelper.reverseArray(value);
		// } catch (IllegalArgumentException e) {
		// e.printStackTrace();
		// throw new RuntimeException(
		// "Most likely programming error, serialization exception",
		// e);
		// }
		// results.add(new SinglePropertyKey(entity.getKind(), property
		// .getKey(), value));
		//
		// }

	}

	// TODO this won't work, it does not write kind neither property name, only
	// value
	/**
	 * Creates key data.
	 * 
	 * @param protoProperty
	 * @return key data or null if should not be indexed
	 */
	private static byte[] composePropertyKey(Property protoProperty) {
		if (!protoProperty.isSearchable())
			return null;
		TupleOutput out = new TupleOutput();
		PropertyValue val = protoProperty.getValue();
		if (val.hasBooleanValue()) {
			out.write(val.isBooleanValue() ? 1 : 0);
		} else if (val.hasDoubleValue()) {
			out.writeDouble(val.getDoubleValue());
		} else if (val.hasInt64Value()) {
			out.writeLong(val.getInt64Value());
		} else if (val.hasPointValue()) {
			out.writeDouble(val.getPointValue().getX());
			out.writeDouble(val.getPointValue().getY());
		} else if (val.hasReferenceValue()) {
			DatabaseEntry entry = new DatabaseEntry();
			EntityBindings.ENTITY_KEY_BINDING.objectToEntry(
					(Key) DataTypeTranslator.getPropertyValue(protoProperty),
					entry);
			out.write(entry.getData());
		} else if (val.hasStringValue()) {
			out.write(val.getStringValueAsBytes());
		} else if (val.hasUserValue()) {
			out.writeString(val.getUserValue().getAuthDomain());
			out.write(val.getUserValue().getEmailAsBytes());
		} else {
			Logger logger = Logger.getLogger(SinglePropertyKeyCreator.class
					+ "");
			logger.log(Level.WARNING,
					"property is not being indexed, not considered as known",
					protoProperty);
			return null;
		}
		return out.toByteArray();
	}
}
